package com.jackrain.nea.shiro.stateful;

import com.jackrain.nea.util.RedisOpsUtil;
import com.jackrain.nea.util.ValueHolder;
import com.jackrain.nea.web.face.SessionInfo;
import com.jackrain.nea.web.face.User;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.CachingSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.SessionCallback;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * Created by Lucky dog on 2016/7/19.
 */
public class CachingShiroSessionDao  extends CachingSessionDAO{

    private static Logger logger= LoggerFactory.getLogger(CachingShiroSessionDao.class.getName());

    // 保存到Redis中key的前缀 prefix+sessionId
    private String prefix = "";

    // 设置会话的过期时间
    private int seconds = 10 * 60 * 1000;

    @Autowired
    private RedisOpsUtil<String,String> redisOpsUtil;

    /**
     * 重写CachingSessionDAO中readSession方法，如果Session中没有登陆信息就调用doReadSession方法从Redis中重读
     */
    @Override
    public Session readSession(Serializable sessionId) throws UnknownSessionException {
        Session session = this.doReadSession(sessionId);
        if (session == null) {
            logger.debug("There is no session with id [" + sessionId + "]");
        }
        return session;
    }

    /**
     * 根据会话ID获取会话
     *
     * @param sessionId 会话ID
     * @return ShiroSession
     */
    @Override
    protected Session doReadSession(Serializable sessionId) {
        Session session = null;
        try {
            session =  (Session) redisOpsUtil.objRedisTemplate.opsForHash().get("session",sessionId);
            logger.debug("sessionId "+sessionId+ " name " +session.getClass().getName()+ " 被读取");
            return session;

        } catch (Exception e) {
            logger.debug("读取Session失败", e.getMessage());
        }
        return session;
    }

    /**
     * 如DefaultSessionManager在创建完session后会调用该方法；
     * 如保存到关系数据库/文件系统/NoSQL数据库；即可以实现会话的持久化；
     * 返回会话ID；主要此处返回的ID.equals(session.getId())；
     */
    @Override
    protected Serializable doCreate(Session session) {
        // 创建一个Id并设置给Session
        Serializable sessionId = this.generateSessionId(session);
        assignSessionId(session,sessionId);
        try {
            session.setTimeout(seconds);
            String sessionKey = "session";
            redisOpsUtil.objRedisTemplate.opsForHash().put(sessionKey,sessionId,session);
            logger.debug("创建Session成功："+session.getId());
        } catch (Exception e) {
            logger.debug("创建Session失败", e.getMessage());
        }
        return sessionId;
    }

    /**
     * 更新会话；如更新会话最后访问时间/停止会话/设置超时时间/设置移除属性等会调用
     */
    @Override
    protected void doUpdate(Session session) {
        try {
            String sessionKey = "session";
            User user = (User) session.getAttribute("user");
            ValueHolder vh = (ValueHolder)session.getAttribute("vh");
            redisOpsUtil.objRedisTemplate.opsForHash().put(sessionKey, session.getId(), session);
            if(user != null){
                String userKey = "user:" + user.getName();
                String clientKey = "client:" + user.getClientId();
                redisOpsUtil.strRedisTemplate.execute(new SessionCallback<Object>(){
                    @Override
                    public List<Object> execute(RedisOperations operations) {
                        operations.multi();
                        operations.opsForList().remove(userKey,-1,session.getId().toString());
                        operations.opsForList().leftPush(userKey, session.getId().toString());
                        operations.opsForList().remove(clientKey,-1,userKey);
                        operations.opsForList().leftPush(clientKey,userKey);
                        return operations.exec();
                    }
                });
                logger.debug("更新Session成功"+session.getId());
            }
        } catch (Exception e) {
            logger.debug("更新Session失败", e.getMessage());
        } finally {

        }
    }

    /**
     * 删除会话；当会话过期/会话停止（如用户退出时）会调用
     **/
    protected void onDelete(Session session) {
        try {
            redisOpsUtil.objRedisTemplate.opsForHash().delete("session",session.getId().toString());
            User user = (User) session.getAttribute("user");
            if(user != null) {
                String userKey = "user:" + user.getName();
                String clientKey = "client:" + user.getClientId();
                String usercntKey = "usercnt:"+user.getName();
                Long index = redisOpsUtil.strRedisTemplate.opsForList().remove(userKey, 1, session.getId().toString());
                if(index > 0){
                    Long count = redisOpsUtil.objRedisTemplate.opsForValue().increment("usercnt:"+user.getName(),-1);
                    if(count <= 0){
                        redisOpsUtil.objRedisTemplate.delete(usercntKey);
                    }
                }
                if (redisOpsUtil.strRedisTemplate.opsForList().size(userKey) == 0){
                    redisOpsUtil.strRedisTemplate.delete(userKey);
                    redisOpsUtil.strRedisTemplate.opsForList().remove(clientKey, 1, userKey);
                    if (redisOpsUtil.strRedisTemplate.opsForList().size(clientKey) == 0){
                        redisOpsUtil.strRedisTemplate.delete(clientKey);
                    }
                }
            }
            logger.debug("删除Session成功");
        }catch (Exception e){
            logger.debug("删除Session失败",e.getMessage());
        }
    }



    @Override
    protected void doDelete(Session session) {}


    /**
     * 删除cache中缓存的Session
     **/
    public void uncache(Serializable sessionId) {}

    /**
     * 获取当前所有活跃用户，如果用户量多此方法影响性能
     **/
    @Override
    public Collection<Session> getActiveSessions() {
        List<Session> sessionList = new ArrayList<Session>();
        List<Object> ls = redisOpsUtil.objRedisTemplate.opsForHash().values("session");
        for (Object o : ls) {
            SessionInfo session = (SessionInfo) o;
            sessionList.add((Session) session.getSession());
        }
        return sessionList;
    }


    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

    public void setSeconds(int seconds) {
        this.seconds = seconds;
    }
}
